use crate::fse::{FseTable, FseDistribution, FseCell};
use crate::byte::*;

use super::{CompressionMode, SeqResult, SeqField, SeqError};

pub enum SeqWorkSpace {
    None,
    Rle(u8),
    Fse(FseTable),
}

impl SeqWorkSpace {
    pub fn new() -> Self {
        Self::None
    }

    pub fn load<R:ByteRead>(&mut self, field:SeqField, mode:CompressionMode, reader:&mut ByteReader<R>, total_len:&mut u32) -> SeqResult<()> {
        match mode {
            CompressionMode::PredefinedMode => { *self = Self::Fse(Self::field_to_default_distribution(field).into()); } ,
            CompressionMode::RLEMode => { *self = Self::Rle(reader.read_to_u8()?); *total_len -= 1; } ,
            CompressionMode::FSECompressedMode => { *self = Self::Fse(FseDistribution::deserialize_from(total_len, reader)?.into()); } ,
            CompressionMode::RepeatMode => {},
        };
        Ok(())
    }

    fn field_to_default_distribution(field:SeqField) -> FseDistribution {
        match field {
            SeqField::LiteralsLength => FseDistribution::predefined_literals_length_distribution(),
            SeqField::MatchLength => FseDistribution::predefined_match_length_distribution(),
            SeqField::OffsetCodes => FseDistribution::predefined_offset_codes_distribution(),
        }
    }

    pub fn first_cell<'a,R:ByteRead>(&self, reader:&mut BitReader<'a,R>) -> SeqResult<FseCell> {
        match self {
            Self::None => Err(SeqError::WorkSpace),
            Self::Rle(byte) => Ok(FseCell { base_line: 0, num_bits: 0, symbol: *byte }),
            Self::Fse(table) => Ok(table.first_cell(reader)?),
        }
    }

    pub fn next_code<'a,R:ByteRead>(&self, reader:&mut BitReader<'a,R>, cell:&mut FseCell) -> SeqResult<u8> {
        match self {
            Self::None => Err(SeqError::WorkSpace),
            Self::Rle(byte) => Ok(*byte),
            Self::Fse(table) => { *cell = table.next_cell(reader, *cell)?; Ok(cell.symbol) },
        }
    }
}


pub(crate) fn lookup_ll_code(code: u8) -> (u32, u8) {
    match code {
        0..=15 => (u32::from(code), 0),
        16 => (16, 1),
        17 => (18, 1),
        18 => (20, 1),
        19 => (22, 1),
        20 => (24, 2),
        21 => (28, 2),
        22 => (32, 3),
        23 => (40, 3),
        24 => (48, 4),
        25 => (64, 6),
        26 => (128, 7),
        27 => (256, 8),
        28 => (512, 9),
        29 => (1024, 10),
        30 => (2048, 11),
        31 => (4096, 12),
        32 => (8192, 13),
        33 => (16384, 14),
        34 => (32768, 15),
        35 => (65536, 16),
        _ => (0, 255),
    }
}

pub(crate) fn lookup_ml_code(code: u8) -> (u32, u8) {
    match code {
        0..=31 => (u32::from(code) + 3, 0),
        32 => (35, 1),
        33 => (37, 1),
        34 => (39, 1),
        35 => (41, 1),
        36 => (43, 2),
        37 => (47, 2),
        38 => (51, 3),
        39 => (59, 3),
        40 => (67, 4),
        41 => (83, 4),
        42 => (99, 5),
        43 => (131, 7),
        44 => (259, 8),
        45 => (515, 9),
        46 => (1027, 10),
        47 => (2051, 11),
        48 => (4099, 12),
        49 => (8195, 13),
        50 => (16387, 14),
        51 => (32771, 15),
        52 => (65539, 16),
        _ => (0, 255),
    }
}